/*
 *	CHANGE LOG
 * 
 * 01.02.01
 *
 *		10/07/2008 - DW - Updated the code to find the exact ModuleID for the Category module
 *						  Instead of relying on it being 1 more than the Help module.
 * 
 * 01.02.02
 *
 *		12/27/2008 - DW - Added code to authorize user for each method call
 * 
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Web;
using DotNetNuke.Modules.Help;
using ITCrossing;
using DotNetNuke.Entities.Users;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Modules;
using DataProvider = DotNetNuke.Data.DataProvider;

namespace ITCrossing
{
	public class metaPostHelpProvider : IPublishable
	{
		# region IPublishable Members

		# region Public Properties

		/// <summary>
		/// ProviderKey should be set to the Friendly Name of the module for
		/// which this provider is being implemented.
		/// </summary>
		public string ProviderKey
		{
			get
			{
				return MetaPostServices.GetFriendlyNameFromModuleDefinition("Help");
			}
		}

		/// <summary>
		/// ManifestFilePath is the path including the filename to the manifest file used to determine 
		/// the settings available in Windows Live Writer.  An example path might
		/// be /DesktopModules/itcMetaPost/manifests/wlwblog.xml.  See the following URL for 
		/// information regarding what Content can be placed in a WLW manifest file:
		/// http://msdn2.microsoft.com/en-us/library/bb463260.aspx
		/// </summary>
		public string ManifestFilePath
		{
			get { return "/DesktopModules/itcMetaPost/manifests/wlwHelp.xml"; }
		}

		/// <summary>
		/// LocalizationFilePath is the path including the filename to the 
		/// resource file used by the module to retrieve the error messages
		/// shown to the user (eg. "/DesktopModules/custommodule/App_LocalResources/customfilename")
		/// Note that the filename should not include the file extension.
		/// </summary>
		public string LocalizationFilePath
		{
			get { return "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPostHelp"; }
		}

		/// <summary>
		/// OPTIONAL - ImageUploadPath is a property metaPost uses to determine where 
		/// to store the media files (images).  This property should return just a 
		/// section of the path starting from the portal root.  Added to this path will
		/// be the username along with the path 
		/// </summary>
		public string ImageUploadPath
		{
			get
			{
				// This can be overridden with custom implementation if needed.  If
				// overridden, then a message should be placed in the ImagePath column
				// for your provider letting administrators know that administration of
				// the ImagePath is implemented elsewhere.  
				return MetaPostServices.GetProviderImageUploadPath(ProviderKey);
			}
		}

		/// <summary>
		/// AttachmentUploadPath is used to specify the location of attachments added
		/// to a post or page.
		/// </summary>
		public string AttachmentUploadPath
		{
			get
			{
				// For now, we'll just set this to the same as the image upload path
				return MetaPostServices.GetProviderAttachmentUploadPath(ProviderKey);
			}
		}

		/// <summary>
		/// SettingsUserControlPath is for future use.  It will return the path to the
		/// the settings user control for this provider.  
		/// </summary>
		public string SettingsUserControlPath
		{
			get { throw new NotImplementedException(); }
		}

		# endregion

		# region Get Methods
		/// <summary>
		/// The purpose of this procedure is to provide a list of all the modules in a 
		/// given portal to which the currently logged on user has either insert, update
		/// or delete access.  Note, this may not always correspond to an actual module.
		/// For example, with the DNN Blog module provider, a list of blogs is returned
		/// along with URL of where each blog is available within the site.  For the News
		/// Articles module, we return a list of all News Articles modules that have been
		/// added to Tabs throughout the site.
		/// </summary>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		/// <param name="providerKey"></param>
		public ModuleInfoStruct[] GetModulesForUser(UserInfo userInfo, PortalSettings portalSettings, string providerKey)
		{
			return MetaPostServices.GetModulesForUser(
				providerKey, userInfo.UserID, portalSettings.PortalId);

		}

		/// <summary>
		/// GetRecentItems returns a list of the recent items added to the module.  For example, with
		/// the blog module, the list would return all the recent blog entries.  For the Text/HTML provider, 
		/// this method would return a list of the recently added Text/HTML modules in the site.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="numberOfItems"></param>
		/// <param name="requestType">GetRecentItems can be called for a number of reasons.  In Windows Live Writer,
		/// users are able to retrieve a list of recent posts or recent pages.  The requestType parameter is used
		/// to denote whether a list of pages or posts is being requested.  Additionally, WLW may request a list 
		/// of hiearchical pages to show pages when allowing a parent page to be assigned to a new page.  The values for this enum
		/// are RecentPosts, RecentPages and RecentPagesHiearchical.</param>
		/// <param name="providerKey"></param>
		/// <returns></returns>		
		public Item[] GetRecentItems(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, int numberOfItems, RecentItemsRequestType requestType, string providerKey)
		{

			// Authorize user for method call
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
							   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			int moduleId = MetaPostServices.GetModuleIdByTabModuleId(Convert.ToInt32(moduleLevelId));

			List<Item> itemArray = new List<Item>();

			HelpController HelpController = new HelpController();
			ArrayList HelpArrayList = HelpController.GetByModules(moduleId);

			if (HelpArrayList.Count > 0)
			{
				int count = 0;
				foreach (TutorialInfo TutorialInfo in HelpArrayList)
				{
					Item item = new Item();
					item.ItemId = TutorialInfo.TutorialID.ToString();
					item.Title = HtmlUtils.StripTags(HttpUtility.HtmlDecode(TutorialInfo.Title), false);
					item.Content = HttpUtility.HtmlDecode(TutorialInfo.Description);
					item.DateCreated = TutorialInfo.LastUpdated;
					itemArray.Add(item);
					count++;
					if (count >= numberOfItems) break;
				}
			}
			else
			{
				throw new MetaPostException("RecentPostsError",
											"There are no Help associated with this user account.  Please enter a user account which has been associated with a blog.");
			}

			return itemArray.ToArray();
		}

		/// <summary>
		/// GetItem is used to retrieve a specific item, which may be a page or a post if your module supports
		/// both concepts.  In cases where a module supports both pages and posts, a concept made available in 
		/// Windows Live Writer, but not in Word 2007 or in many other publishing clients, the itemType parameter 
		/// can be used to distinguish between when this method is being called to add a page or a post.  If your 
		/// provider doesn't support a distinction between pages and posts, then simply ignore the itemType parameter.
		/// </summary>
		/// <param name="itemId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public Item GetItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
		{
			Item item = new Item();

			HelpController HelpController = new HelpController();
			TutorialInfo TutorialInfo = HelpController.Get(Convert.ToInt32(itemId));

			// Authorize user for method call
			string moduleId = GetModuleIdFromItemId(itemId, portalSettings.PortalId);
			MetaPostServices.AuthorizeUser(AuthorizationType.DnnModuleId, moduleId,
							   GetModulesForUser(userInfo, portalSettings, ProviderKey));			
			
			item.Title = HttpUtility.HtmlDecode(TutorialInfo.Title);
			item.Content = HttpUtility.HtmlDecode(TutorialInfo.Description);
			item.Summary = HttpUtility.HtmlDecode(TutorialInfo.ShortDesc);

			item.DateCreated = TutorialInfo.LastUpdated;
			item.ItemId = TutorialInfo.TutorialID;

			// Retrieve the categories for this post
			ItemCategory[] itemCategories =
				GetItemCategories(item.ItemId.ToString(), userInfo, portalSettings);
			List<string> catNamesToAdd = new List<string>();
			foreach(ItemCategory cat in itemCategories)
			{
				catNamesToAdd.Add(cat.CategoryName);
			}
			item.Categories = catNamesToAdd.ToArray();

			return item;
		}

		# endregion 

		# region Add, Edit and Delete Methods

		/// <summary>
		/// EditItem is called to edit either a page or a post.  The difference
		/// between the two is denoted in a property of the Item struct (ItemType).
		/// An additional property of the Item struct that may be important to the 
		/// implementation of the EditItem method is the Publish property.  This is 
		/// set to true when the item should be published.  The default setting of 
		/// false denotes that the item may be saved as a draft.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public string NewItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
		{

			// Authorize user for method call
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
							   GetModulesForUser(userInfo, portalSettings, ProviderKey));			
		
			List<string> images = MetaPostServices.GetImageListFromItem(ref item);

			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			int moduleId = MetaPostServices.GetModuleIdByTabModuleId(Convert.ToInt32(moduleLevelId));

			// Tell the UpdateHelp procedure that we're adding a new Help.
			item.ItemId = "-1";
			int TutorialId = UpdateHelp(item, moduleId, userInfo);

			MetaPostServices.SaveItemImages(TutorialId, images, ProviderKey, portalSettings, userInfo.UserID);

			return TutorialId.ToString();
		}

		/// <summary>
		/// EditItem is called to edit either a page or a post.  The difference
		/// between the two is denoted in a property of the Item struct (ItemType).
		/// An additional property of the Item struct that may be important to the 
		/// implementation of the EditItem method is the Publish property.  This is 
		/// set to true when the item should be published.  The default setting of 
		/// false denotes that the item may be saved as a draft.
		///
		/// It's important to note that when Windows Live Writer sends the information 
		///		for a post edit, the moduleLevelId is not passed.  For implementation of 
		///		this procedure in providers which do not support pages, the moduleLevelId 
		///		should be ignored and attempts to get the moduleId can be made by calling
		///		GetModuleIdFromItemId or a similar private procedure.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool EditItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
		{

			List<string> images = MetaPostServices.GetImageListFromItem(ref item);

			// Get the actual ModuleId 
			string moduleId = GetModuleIdFromItemId((string)item.ItemId, portalSettings.PortalId);

			// Authorize user for method call
			MetaPostServices.AuthorizeUser(AuthorizationType.DnnModuleId, moduleId.ToString(),
							   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			UpdateHelp(item, Convert.ToInt32(moduleId), userInfo);

			MetaPostServices.SaveItemImages(Convert.ToInt32(item.ItemId), images, ProviderKey, portalSettings, userInfo.UserID);

			return true;
		}

		/// <summary>
		/// DeleteItem is used to delete either a page or a post.  The difference between the two is noted 
		/// in the itemType parameter which is an enumeration set to either Page or Post.
		/// </summary>
		/// <param name="itemId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>		
		public bool DeleteItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
		{
			
			HelpController helpController = new HelpController();
			int tutorialId = Convert.ToInt32(itemId);
			string moduleId = GetModuleIdFromItemId(itemId, portalSettings.PortalId);

			// Authorize user for method call
			MetaPostServices.AuthorizeUser(AuthorizationType.DnnModuleId, moduleId,
							   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			// Retrieve the categories for this item
			int[] catIds = helpController.TutorialCategoryList(tutorialId);

			// Clean up the categories related to the item we are about to delete.
			foreach (int catId in catIds)
			{
				helpController.TutorialDeleteCategory(tutorialId, catId);	
			}
			helpController.Delete(tutorialId);

			return true;
		}

		# endregion

		# region Category Related Procedures

		/// <summary>
		/// GetCategories retrieves a list of categories for a particular module.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>		
		public ItemCategoryInfo[] GetCategories(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings)
		{
			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			int moduleId = MetaPostServices.GetModuleIdByTabModuleId(Convert.ToInt32(moduleLevelId));

			List<ItemCategoryInfo> catInfoList = new List<ItemCategoryInfo>();

			CategoryController CategoryController = new CategoryController();
			
			// Get the categories from the controller
			int categoryModuleId = GetCategoryModuleID(moduleId);
			CategoryInfoCollection HelpCategories = CategoryController.CategoryListActive(categoryModuleId);
				
			foreach (CategoryInfo Cat in HelpCategories)
			{
				ItemCategoryInfo catInfo = new ItemCategoryInfo();
				catInfo.CategoryId = Cat.CategoryID;
				catInfo.CategoryName = Cat.Name;
				catInfo.Description = Cat.Description;
				catInfo.HtmlUrl = "";
				catInfo.RssUrl = "";
				catInfo.ParentId = Cat.Parent;
				catInfoList.Add(catInfo);
			}
			return catInfoList.ToArray();

		}

		
		public bool SetItemCategories(string itemId, ItemCategory[] itemCategories, UserInfo userInfo, PortalSettings portalSettings)
		{
			
			// First of all, get a list of the current categories.  
			HelpController helpController = new HelpController();
			int[] currentCategoryIds = helpController.TutorialCategoryList(Convert.ToInt32(itemId));
			List<int> catToAdd = new List<int>();
			List<int> catToDelete = new List<int>();
			int tutorialId = Convert.ToInt32(itemId);

			// See which categories are not in the list
			foreach (int catId in currentCategoryIds) // Loop through the existing categories
			{
				bool catIsInList = false;
				foreach(ItemCategory ic in itemCategories) // Loop through the new categories
				{
					if (ic.CategoryId == catId.ToString())
					{
						catIsInList = true;
						break;
					}
				}
				if (!catIsInList)
				{
					catToDelete.Add(catId);
				}
			}

			// See which categories are in the list
			foreach (ItemCategory ic in itemCategories) // Loop through new categories
			{
				bool catIsInList = false;
				foreach(int catId in currentCategoryIds) // Loop through existing categories
				{
					if (ic.CategoryId == catId.ToString())
					{
						catIsInList = true;
						break;
					}
				}
				if (!catIsInList)
				{
					catToAdd.Add(Convert.ToInt32(ic.CategoryId));
				}
			}

			// Add new categories
			foreach (int catId in catToAdd)
			{
				// Get the maximum view order
				int newViewOrder = helpController.GetMaximumTutorialCategoryViewOrder(catId);
				helpController.TutorialAddToCategory(tutorialId, catId, newViewOrder);
			}

			// Remove old categories no longer needed
			foreach(int catId in catToDelete)
			{
				helpController.TutorialDeleteCategory(tutorialId, catId);
			}

			return true;
		}

		public ItemCategory[] GetItemCategories(string itemId, UserInfo userInfo, PortalSettings portalSettings)
		{
			List<ItemCategory> itemCategories = new List<ItemCategory>();
			int moduleId = Convert.ToInt32(GetModuleIdFromItemId(itemId, portalSettings.PortalId));

			HelpController HelpController = new HelpController();
			CategoryController CategoryController = new CategoryController();
			TutorialInfo HelpInfo = HelpController.Get(Convert.ToInt32(itemId));

			if (HelpInfo.TutorialID != -1)
			{
				ItemCategory ic;
				int[] categories = HelpController.TutorialCategoryList(HelpInfo.TutorialID);
				foreach (int catId in categories)
				{
					CategoryInfo ci = CategoryController.CategoryGet(catId);

					ic = new ItemCategory();
					ic.CategoryId = ci.CategoryID.ToString();
					ic.CategoryName = ci.Name;
					itemCategories.Add(ic);
				}
			}

			return itemCategories.ToArray();

		}

		/// <summary>
		/// Used to add a new category for modules which support this feature.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="category"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		public int NewCategory(string moduleLevelId, NewItemCategory category, UserInfo userInfo, PortalSettings portalSettings)
		{
			int categoryId = 0;
			// Check first to see if the user should have the ability to add Categories

			ModuleController moduleController = new ModuleController();
			//ModuleInfo moduleInfo =
			//    moduleController.getm(portalSettings.PortalId, moduleName);
			//bool bHasAdminRights = PortalSecurity.IsInRoles(_moduleConfiguration.AuthorizedEditRoles);
			//Dim blnHasModuleEditPermissions As Boolean = PortalSecurity.IsInRoles(_moduleConfiguration.AuthorizedEditRoles)
			throw new MetaPostException("FeatureNotImplemented",
										"This feature is currently not implemented.");

			return categoryId;
		}

		# endregion

		#endregion

		# region Private Procedures Specific to Help Module

		/// <summary>
		/// GetModuleIdFromItemId is needed for the Help provider for the GetItem method which calls it.
		/// </summary>
		/// <param name="itemId"></param>
		/// <returns></returns>
		/// <param name="portalId"></param>
		private string GetModuleIdFromItemId(string itemId, int portalId)
		{
			int moduleId;
			// Get the moduleLevelId from the itemId
			object[] methodParams = { itemId };
			string SQL = "itcmetapost_help_get_moduleid_by_itemid";
			moduleId = (int)DataProvider.Instance().ExecuteScalar(SQL, methodParams);
			return moduleId.ToString();
			return null;
		}

		/// <summary>
		/// UpdateHelp is the procedure which handles updating the database with the
		/// changes made to the Help entry.
		/// </summary>
		/// <param name="item"></param>
		/// <param name="moduleId"></param>
		/// <param name="userInfo"></param>
		/// <returns></returns>
		private int UpdateHelp(Item item, int moduleId, UserInfo userInfo)
		{
			HelpController HelpController = new HelpController();
			CategoryController CategoryController = new CategoryController();
			
			int TutorialId = 0;

			TutorialInfo TutorialInfo = new TutorialInfo();

			TutorialInfo.TutorialID = (item.ItemId.ToString() == "-1") ? -1 : Convert.ToInt32(item.ItemId);

			TutorialInfo.ShortDesc = item.Content;
			TutorialInfo.Description = (string.IsNullOrEmpty(item.ExtendedContent))
								? item.Content
								: item.ExtendedContent;
			TutorialInfo.LastUpdated = DateTime.Now;
			TutorialInfo.Title = item.Title;
			TutorialInfo.ModuleID = Convert.ToInt32(moduleId);

			if (TutorialInfo.TutorialID == -1)
			{
				TutorialId = HelpController.Add(TutorialInfo);	
			}
			else
			{
				HelpController.Update(TutorialInfo);
				TutorialId = TutorialInfo.TutorialID;
			}

			// Check to see if we have a category selected
			int categoryModuleId = GetCategoryModuleID(moduleId);
			CategoryInfoCollection categories = CategoryController.CategoryListActive(categoryModuleId);
			List<ItemCategory> itemCategories = new List<ItemCategory>();
			if (item.Categories.Length > 0)
			{
				// Create an ItemCategory array and send this to the SetItemCategories Method
				
				foreach (CategoryInfo cat in categories)
				{
					foreach (string catInItem in item.Categories)
					{
						if (cat.Name == catInItem)
						{
							ItemCategory ic = new ItemCategory();
							ic.CategoryId = cat.CategoryID.ToString();
							ic.CategoryName = cat.Name;
							itemCategories.Add(ic);
							break;
						}
					}
				}
			}
			else
			{
				if (item.StyleDetectionPost)
				{
					// Set to first category in the list
					if (categories.Count > 0)
					{
						ItemCategory ic = new ItemCategory();
						ic.CategoryId = categories[0].CategoryID.ToString();
						ic.CategoryName = categories[0].Name;
						itemCategories.Add(ic);
					}
				}
				else
				{
					throw new MetaPostException("NoCategorySelected", "Please select a category.");
				}
			}
			// Now that we have the ItemCategory array, we'll call SetItemCategories
			SetItemCategories(TutorialId.ToString(), itemCategories.ToArray(), null, null);
			
			return TutorialId;

		}

		/// <summary>
		/// Returns the moduleID for the associated Categories module that has 
		/// been added to the same tab
		/// </summary>
		/// <returns></returns>
		private int GetCategoryModuleID(int moduleID)
		{
			int retModID = 0;
			
			// Check to see if a Category ModuleID has been passed in
			
			if (HttpContext.Current.Request != null && HttpContext.Current.Request["cmid"] != null)
			{
				retModID = Convert.ToInt32(HttpContext.Current.Request["cmid"]);
			}
			else
			{

				string SQL = "SELECT Min(m.ModuleID) as ModuleID FROM {databaseOwner}{objectQualifier}Modules m1 (nolock) " +
				"	JOIN {databaseOwner}{objectQualifier}TabModules tm1 (nolock) ON tm1.ModuleID = m1.ModuleID " +
				"	JOIN {databaseOwner}{objectQualifier}ModuleDefinitions md1 (nolock) ON md1.ModuleDefID = m1.ModuleDefID " +
				"	JOIN {databaseOwner}{objectQualifier}ModuleDefinitions md2 (nolock) on md2.DesktopModuleID = md1.DesktopModuleID AND md2.FriendlyName = 'Category' " +
				"	JOIN {databaseOwner}{objectQualifier}Modules m (nolock) ON m.ModuleDefID = md2.ModuleDefID " +
				"	JOIN {databaseOwner}{objectQualifier}TabModules tm (nolock) ON tm.ModuleID = m.ModuleID " +
				" WHERE tm.TabID = tm1.TabID AND m1.ModuleID = " + moduleID.ToString();

                using (IDataReader dr = DataProvider.Instance().ExecuteSQL(SQL))
                {
                    if (dr.Read())
                    {
                        retModID = dr.GetInt32(dr.GetOrdinal("ModuleID"));
                    }
                }
				
			}

			return retModID;

		}

		# endregion

	}
}